#pragma once
#include <windows.h>
#include <d3dx9.h>

#include "..\common\def.h"
#include "..\common\types.h"
#include "..\common\common.h"
#include "..\math\vector.h"
#include "..\math\plane.h"
#include "..\math\sphere.h"
#include "..\engine\camera.h"
#include "..\common\string.h"

#include "d3d_util.h"
#include "d3d_light.h"
#include "d3d_image.h"
#include "d3d_mesh.h"
#include "d3d_pointsprite.h"
#include "d3d_particles.h"
#include "d3d_texture.h"
#include ".\shaders\d3d_shader.h"

struct D3DWaterShader; 
struct D3DScreenShader; 
struct D3DLightShader; 
struct D3DDepthMapShader; 
struct D3DParticleShader; 

#ifdef _DEBUG
#define D3D_DEBUG_INFO
#define PIX(mgs) { LPCWSTR pMsg = mgs; D3DPERF_SetMarker(D3DCOLOR_COLORVALUE(1,0,0,1), pMsg); }
#else
#define PIX(mgs) {}
#endif

#define DEVICE_TEXTURE_UNITS 8

class EE_EXPORT CD3D9Device
{                        
	public:
		enum EE_EXPORT RenderMode { SURFACE_NORMAL, SURFACE_DEPTH, LIGHTING, VELOCITY, DEFAULT, REFLECTION }; 
		enum EE_EXPORT ResourceFilter { RESOURCE_ALL = 0xffffffff, RESOURCE_TEXTURE = 1, RESOURCE_MODEL = 2,
		RESOURCE_POINTSPRITE = 4, RESOURCE_PARTICLESYSTEM = 8, RESOURCE_LIGHT = 16,
		RESOURCE_SHADER = 32, RESOURCE_SCREENSPRITE = 64}; 
		

	private:

	//For creation of render targets
	friend struct D3DDirectionalLight; 
	//For texture unit access
	friend struct D3DShader; 
	//For light shader access
	friend struct D3DMesh; 
	//For access to RTs
	friend struct D3DWaterShader; 
	friend struct D3DScreenShader; 
	friend struct D3DLightShader; 
	friend struct D3DDepthMapShader; 
	friend struct D3DParticleShader; 
	friend struct D3DReflectShader; 
	//For VB creation
	friend struct D3DParticleSystem; 
	friend struct D3DSpriteShader; 
	friend struct D3DScreenSpriteShader; 
	friend struct D3DPointSpriteGroup; 

	/***************************************************************
	 * Fields
	 ***************************************************************/

	/********************************
	 * Core
	 */
	LPDIRECT3D9				m_pD3D; 
	LPDIRECT3DDEVICE9		m_pD3DDevice; 

	/********************************
	 * Resources
	 */
	ArrayList<D3DShader*>	m_Shaders; 
	ArrayList<Texture*>	m_Textures; 
	ArrayList<D3DImage*>	m_Sprites; 
	ArrayList<D3DParticleSystem*>	m_ParticleSystems; 
	ArrayList<D3DLight*>	m_Lights; 
	ArrayList<D3DDirectionalLight*>	m_DirectionalLights; 
	ArrayList<D3DModel*>	m_Models; 
	ArrayList<D3DPointSpriteGroup*>	m_PointspriteInstances; 

	/********************************
	 * Shaders
	 */
	D3DShader* m_pDefaultShader; 
	D3DDepthMapShader* m_pDepthShader; 
	D3DScreenSpriteShader* m_pScreenSpriteShader;
	D3DScreenShader* m_pScreenShader; 
	D3DShader* m_pSkyShader; 
	D3DLightShader* m_pLightShader; 
	D3DXHANDLE m_pScreenColorOverride;

	/********************************
	 * Textures
	 */
	D3DTexture3D* m_pEnvironmentCubemap;
	LPDIRECT3DTEXTURE9 m_pLightTexture; 
	Texture* m_pDefaultAOTexture;
	Texture* m_pDefaultNMTexture;
	Texture* m_pWhiteTexture; 
	Texture* m_pErrorTexture; 
	LPDIRECT3DBASETEXTURE9 m_deviceTextures[DEVICE_TEXTURE_UNITS];

	/********************************
	 * Render Targets
	 */
	RenderTarget* m_pRTPixelBuffer; 
	RenderTarget* m_pRTBloomBuffer; 
	RenderTarget* m_pRTRefractionBuffer; 
	RenderTarget* m_pRTReflectionBuffer; 
	RenderTarget* m_pRTPostProcessBuffer;

	/********************************
	 * Rendering
	 */
	LPD3DXSPRITE m_pSpriteBatch; 
	SunData m_SunData;
	D3DMesh* m_pSkyDome; 
	D3DXPLANE m_ReflectionPlane;
	LPD3DXMESH m_pSunSphere;
	bool m_RenderReflections;
	bool m_RenderSky;
	bool m_RenderLensFlare;
	D3DCOLOR m_BackgroundColor; 
	RenderMode m_RenderMode; 	
	ViewOrrientation m_camera; 	
	
	/********************************
	 * Misc
	 */
	LPDIRECT3DQUERY9 m_pOcclusionQuery;
	DeviceSettings m_deviceSettings; 
	ScreenText* m_pDefaultFont; 
	LPDIRECT3DSURFACE9		m_pBackBufferSurface; 
	LPDIRECT3DVERTEXBUFFER9 m_pScreenQuadVB; 
	LPDIRECT3DVERTEXDECLARATION9 m_pCurrentDeclaration;
	LPDIRECT3DVERTEXDECLARATION9 m_pVertexDeclaration; 
	static const D3DXMATRIX m_IdentityMatrix; 

	/********************************
	 * Debug rendering
	 */
#ifdef _DEBUG
	struct DebugLine { 
		Vector3f v1;
		Vector3f v2;
		Color3f c;
	};
	struct DebugPoint { 
		Vector3f p;
		Color3f c;
		float r;
	};
	ArrayList<DebugLine> m_dbgLines;
	ArrayList<DebugPoint> m_dbgPoints;
	ArrayList<DebugPoint> m_dbgSpheres;

	void RenderDebug();
#endif

	/***************************************************************
	 * Functions
	 ***************************************************************/
	
	/********************************
	 * Creation
	 */
	HRESULT	createCubemapTexture(const string &sz_filename, Texture **texturePtr); 
	HRESULT createRenderTarget( RenderTarget *renderTarget, 
								unsigned int width, unsigned int height, 
								D3DFORMAT colorFormat, bool hasDepthStencil); 
	HRESULT createTextFont(ScreenText *renderText, const char* fontFace); 
	HRESULT	createDefaultShaders();
	bool	CreateDevice(LPDIRECT3D9 pD3DInterface, bool fullscreen, int vertexProcessing, D3DDISPLAYMODE& mode, D3DDEVTYPE type, D3DFORMAT format, LPDIRECT3DDEVICE9* pOut); 
	HRESULT	createDeviceResources(); 
	HRESULT CreateScreenGeometry();

	
	/********************************
	 * Releasing
	 */ 
	HRESULT	releaseResources(void); 
	HRESULT	releaseDefaultShaders();
	HRESULT ReleaseScreenGeometry();

	/********************************
	 * Misc
	 */
	void UpdateViewFrustum(ViewOrrientation& view); 
	HRESULT EnumerateDevice(LPDIRECT3D9 pD3DInterface, DeviceSettings& settings, LPDIRECT3DDEVICE9* pOut); 
	void OcclusionQueries();
	void RenderReflectionBuffer();
	void RenderRefractionBuffer();
	void RenderPixelBuffer();
	void RenderScreenText(); 
	void RenderScreenItems(); 
	void RenderDepthMap(RenderTarget* pDepthBuffer, ViewOrrientation *pView); 
	void RenderScreenGeometry();
	void RenderBloomFiltering();
	LPDIRECT3DBASETEXTURE9 GetTexture(cuint unit);


public:
	
	CD3D9Device(); 
	virtual ~CD3D9Device(); 

	HRESULT	Initailize(DeviceSettings& settings); 
	HRESULT	Release(); 
	void PreformRendering(); 


	HRESULT SearchForResource(const string& identity, IResource **pResource, const ResourceFilter filter = RESOURCE_ALL); 
	void ReloadResources(ResourceFilter filter); 
		
	/*static bool PointInFrustum(ViewOrrientation* view,D3DXVECTOR3 &p);  
	static bool PointsInFrustum(ViewOrrientation* view,Vector3f* p, uint count);  
	static bool BoundSphereInFrustum(ViewOrrientation* view, Spheref* pBoundsSphere);  */

	void SetRenderMode(const RenderMode mode); 
	void SetSunParameters(const SunData& sun);
	void SetFogParamters(const FogData& fog);
	void SetSkydome(D3DMesh* pSkyDome); 
	void SetReflectionPlane(const Planef& plane); 
		
	void SetTexture(const unsigned int unit,Texture* const pTexture);
	void SetTexture(const unsigned int unit,LPDIRECT3DBASETEXTURE9 pTexture);
	void SetCurrentVertexDeclaration(LPDIRECT3DVERTEXDECLARATION9 pDeclaration);

	LPDIRECT3DDEVICE9	GetDevice(); 
	RenderMode GetRenderMode(); 
	Texture* GetWhiteTexture(); 
	Texture* GetDefaultAOTexture(); 
	Texture* GetDefaultNMTexture();

	D3DTexture3D* GetEnvironmentCubemap(); 
	void SetEnvironmentCubemap(D3DTexture3D* pCubemap);
	Texture* GetErrorTexture();
	LPDIRECT3DTEXTURE9 GetDeferredLightTexture();
	D3DShader* GetDefaultShader();
	ViewOrrientation* GetViewOrrientation(); 	
	DeviceSettings GetDeviceSettings() const;


	void AddRenderQue(D3DRenderable* const pMesh, const Matrix4f& transform); 
	void AddRenderQue(D3DRenderable* const pMesh, const Matrix4f& transform, D3DShader* const m_pShader); 

	HRESULT CreateModel(const string &filename, D3DModel **pOut);
	HRESULT CreateModel(D3DModelLOD** pLODData, unsigned int LODcount, string& identifier, D3DModel **pOut);
	HRESULT	CreatePointsprite(const string &filename, D3DSprite** pSpriteInstance); 
	HRESULT CreatePointsprite(const string &filename, const float radius, const Vector3f& position,  D3DSprite** pSpriteInstance); 

	HRESULT CreateTexture(const string *pFilenames, const int frameCount, const float frameRate, Texture **texturePtr);
	HRESULT	CreateTexture(const string &sz_filename, Texture **texturePtr); 
	HRESULT CreateLight(const Vector3f& position, const Color3f& diffuseColor, const Color3f& ambientColor, float range,D3DLight **pOut); 
	HRESULT CreateDirectionalLight(const Vector3f& position, const Vector3f& direction, const Color3f& diffuseColor, const Color3f& ambientColor, float range,D3DDirectionalLight **pOut); 
	HRESULT CreateDirectionalLight(D3DDirectionalLight **pOut); 
	HRESULT CreateLight(D3DLight **pOut); 
	HRESULT	CreateShader(D3DShader **pOut, const string& szFilename); 
	HRESULT CreateParticleSystem(D3DParticleSystem** pOut,const string& pTextureFilename); 
	HRESULT CreateParticleSystem(D3DParticleSystem** pOut,Texture *pTexture); 
	HRESULT CreateSprite(D3DImage** pOut,const string& filename); 
	
	HRESULT DeregisterResource(IResource* const pResource); 

	void WriteLine(string &str, ScreenText::TextFace flags = ScreenText::DEFAULT_FACE); 
	void WriteLine(ScreenText::TextFace flags,const char* format,...); 

	
#ifdef _DEBUG
	void DrawQuad(D3DXPLANE p); 
	void DrawLine(const Vector3f& start,const Vector3f& end, const Color3f& color = Color3f(1.0f,0.0f,0.0f)); 
	void DrawPoint(const Vector3f& position, const float radius = 1.0f, const Color3f& color = Color3f(1.0f,0.0f,0.0f)); 
	void DrawSphere(const Vector3f& position, const float radius = 1.0f, const Color3f& color = Color3f(1.0f,0.0f,0.0f)); 
#endif
}; 
